﻿var Consts = {
    controlValue: "__ControlValue",
    control: "__Control",
    identity: "Identity",
    primaryKey: "Id",
    displayValue: "Name",
    rows: "Rows",
    code: "Code",
    msg: "Message",
    url: "Url",
    exception: "Exception",
    token: "Token",
    deviceId: "DeviceId",
    clientId: "ClientId",
    data: "Data",
}

var Guid = {
    newGuid: function () {
        var text = "0123456789abcdef";

        var result = "";

        for (var i = 0; i < 32; i++) {
            result += text.charAt(Math.floor(Math.random() * 16));
        }

        return result;
    }
};

var SignBuilder = function () {

    var builder = "";

    this.computeSign = function () {

        console.log(builder);

        return app.md5(builder);
    }

    this.append = function (name, value) {

        if (name == "ClientTime") {
            value = Date.parse(value) / 1000/* Get unix time seconds */;
        }

        if (value instanceof Date) {
            value = value.toISOString();
        }

        if (value === null) {
            value = "";
        }

        if (builder) {
            builder += "&";
        }

        builder += name;
        builder += "=";
        builder += value;
    }
};

var JSONFormat = {
    contentType: "application/json",
    encode: function (source) {
        source = this.asValues(source);
        return JSON.stringify(source);
    },
    decode: function (text) {
        return eval("(" + (text || "") + ")");
    },
    asValues: function (object) {
        if (app.isElementObject(object, "INPUT")) {
            return object;
        }

        if (object instanceof Date) {
            return object.toISOString();
        } else if (object instanceof Array) {
            for (var i = 0; i < object.length; i++) {
                object[0] = this.asValues(object[0]);
            }
        } else if (typeof object == "object") {
            for (var name in object) {
                object[name] = this.asValues(object[name]);
            }
        }

        return object;
    },
    getString: function (object) {
        return this.encode(object);
    }
}

var FormDataFormat = {
    encode: function (source) {
        source = JSONFormat.asValues(source);
        var formData = new FormData();

        for (var name in source) {
            var value = source[name];
            if (app.isElementObject(value, "INPUT")) {
                var values = this.getElementValues(value);

                for (var i = 0; i < values.length; i++) {
                    formData.append(name, values[i]);
                }
            } else {
                formData.append(name, value);
            }
        }

        return formData;
    },
    getElementValues: function (element) {
        switch (element.type) {
            case "file":
                return element.files || [element];
            default:
                return [$(element).val()];
        }
    },
    decode: function (text) {
        try {
            return JSONFormat.decode(text);
        } catch (e) {
            
        }

        try {
            return ESONFormat.decode(text);
        } catch (e) {

        }

        return {};
    }
}

var app = {
    useIFrameSubmit: false,
    serverRemove: "3A1EE3A498CF4858848B7FDE25F6F850",
    client_id: null,
    device_id: null,
    token: null,
    serverAsync: true,
    disabledTableLoad: false,
    body: $(document.body),
    defaultFormat: JSONFormat,
    defaultFailure: function () {

    },
    defaultSuccess: function () {

    },
    buffered: {},
    show: function (tempFrame) {
        tempFrame = $(tempFrame);

        var frame = tempFrame.clone(true);

        frame.prop("id", "");

        frame.removeClass("temp");

        function setZIndex(Me) {
            var zIndex = app._ZIndex = (app._ZIndex || 0) + 1;

            Me.css("z-index", zIndex);
        }

        setZIndex(frame);

        frame.mousedown(function () {
            setZIndex($(this));
        });

        frame.find(".close").click(function () {
            frame.triggerHandler("close");

            frame.remove();
        });

        frame.find(".max").click(function () {
            if (frame.hasClass("minimized")) {
                frame.removeClass("minimized");
            } else {
                frame.toggleClass("maximized");
            }

            frame.trigger("resize");
        });

        frame.find(".min").click(function () {
            frame.addClass("minimized");
            frame.removeClass("maximized");

            frame.trigger("resize");
        });

        frame.find(".top").mousedown(function (e) {
            if (frame.hasClass("maximized")) {
                return;
            }

            var target = $(e.target);

            if (target.hasClass("min")
                || target.hasClass("max")
                || target.hasClass("close")
                || target.hasClass("right")) {
                return;
            }

            var win = $(document);

            var removeGrip = $(document.createElement("div"));


            var oX = e.clientX;
            var oY = e.clientY;

            var wWidth = win.width();
            var wHeight = win.height();

            var offset = frame.offset();

            var fWidth = frame.outerWidth();
            var fHeight = frame.outerHeight();

            removeGrip.addClass("remove-grip");

            /* 减去 Border 的宽度和高度 10 */
            removeGrip.width(fWidth - 10);
            removeGrip.height(fHeight - 10);

            move(removeGrip, offset.left, offset.top);

            app.body.append(removeGrip);

            function move(ele, x, y) {
                x = Math.max(x, 0);
                x = Math.min(x, wWidth - fWidth);
                y = Math.max(y, 0);
                y = Math.min(y, wHeight - fHeight);

                ele.css("left", Math.floor(x) + "px");
                ele.css("top", Math.floor(y) + "px");
            }

            function mouseMove(e) {
                e.preventDefault();

                move(removeGrip, e.clientX - oX + offset.left, e.clientY - oY + offset.top);
            }
            function mouseUp(e) {
                e.preventDefault();

                frame.css("margin-left", "0px");
                frame.css("margin-top", "0px");

                var offset = removeGrip.offset();

                move(frame, offset.left, offset.top);

                frame.triggerHandler("move");

                win.unbind("mousemove", mouseMove);
                win.unbind("mouseup", mouseUp);

                removeGrip.remove();
            }
            win.mousemove(mouseMove);
            win.mouseup(mouseUp);
        });

        app.body.append(frame);

        /* 页面 window 大小不能容下 frame 时，调用最大化按钮。 */
        if ($(window).width() < frame.width() || $(window).height() < frame.height()) {
            frame.find(".max").click();
        }

        frame.resize(function () { app.contentLayout(frame); });

        app.contentExtend(frame);
        app.contentLayout(frame);

        frame.triggerHandler("show");

        return frame;
    },
    showPhoto: function (url) {
        window.open(url);
    },
    contentLayout: function (content) {
        content.find(".content .fit").each(function () {
            var me = $(this);
            var parent = me.parent();
            me.outerWidth(parent.width());
            me.outerHeight(parent.height() - 44);
        });
    },
    contentExtend: function (content) {
        if (window._IsSupportPlaceholder === undefined) {
            window._IsSupportPlaceholder = "placeholder" in document.createElement("input");
        }

        if (!window._IsSupportPlaceholder) {
            if (!window._InitJQueryValMethod) {
                window._InitJQueryValMethod = true;
                $.fn.OriginalVal = $.fn.val;

                $.fn.val = function (v) {
                    if (v == undefined && this.hasClass("placeholder") && $(this).hasClass("no-value") && $(this).OriginalVal() == $(this).attr("_Placeholder")) {
                        return "";
                    }

                    return $.fn.OriginalVal.apply(this, arguments);
                }
            }
            content.find("input[type=text][placeholder]:not(.placeholder),textarea[placeholder]:not(.placeholder)").each(function () {
                var me = $(this);

                me.addClass("placeholder");

                if (me.prop("type") == "password") {
                    return;
                }

                me.attr("_Placeholder", me.attr("placeholder"));

                function meBlur() {
                    var me = $(this);
                    if (me.val()) {
                        me.removeClass("no-value");
                    } else {
                        me.addClass("no-value");
                        me.val(me.attr("_Placeholder"));
                    }
                }

                function meFocus() {
                    var me = $(this);
                    if (me.hasClass("no-value") && !me.val()) {
                        me.val("");
                    }
                }


                me.focus(meFocus);
                me.change(meBlur);
                me.blur(meBlur);
                me.blur();
            });
        }
    },
    apply: function (form, data) {
        form = $(form);

        form.prop("data", data);

        form.triggerHandler("apply", [data]);

        form.find("[name]").addClass("temp-name-item");
        form.find(".many .temp-name-item").removeClass("temp-name-item");

        for (var name in data) {
            var element = form.find(".temp-name-item[name='" + name + "']");

            if (element.length == 0) {
                continue;
            }

            var value = data[name];

            element.each(function (index) {
                if (this.tagName == "INPUT" && (this.type == "checkbox" || this.type == "radio")) {
                    this.checked = value instanceof Array ? value.indexOf($(this).val()) != -1 : value == $(this).val();
                } else {
                    var itemValue = value instanceof Array ? value[index] : value;

                    switch (this.tagName) {
                        case "INPUT":
                        case "SELECT":
                        case "TEXTAREA":
                            $(this).val(itemValue);
                            this[Consts.controlValue] = itemValue;

                            $(this).change();
                            break;
                        case "VIDEO":
                        case "IMG":
                            this.src = itemValue;
                    }
                }
            });
        }

        form.find(".temp-name-item").removeClass("temp-name-item");

        form.find("[display-where]").each(function () {
            var me = $(this);
            var result = eval(me.attr("display-where"));

            if (result) {
                me.removeClass("ignore");
            } else {
                me.addClass("ignore");
            }
        });

        if (!form.hasClass("applyed")) {
            form.addClass("applyed");

            form.find("[disabled]").addClass("force-disabled").removeAttr("disabled");
        }

        form.find("[readonly-where]").each(function () {
            var me = $(this);
            var result = eval(me.attr("readonly-where"));

            if (result) {
                me.attr("readonly", "readonly");

                me.find("input").attr("disabled", "disabled");
                me.find("textarea").attr("disabled", "disabled");
                me.find(".link").addClass("ignore");
                me.find("select").attr("disabled", "disabled");
                me.find(".image-upload").addClass("disabled");
                me.find(".file-upload").addClass("disabled");
                me.find(".delete").addClass("ignore");
            } else {
                me.removeAttr("readonly");

                me.find("input").removeAttr("disabled");
                me.find("textarea").removeAttr("disabled");
                me.find(".link").removeClass("ignore");
                me.find("select").removeAttr("disabled");
                me.find(".image-upload").removeClass("disabled");
                me.find(".file-upload").removeClass("disabled");
                me.find(".delete").removeClass("ignore");
            }
        });

        form.find(".force-disabled").attr("disabled", "disabled");
    },
    now: function () {
        return new Date(Date.now());
    },
    server: function (data, success, failure, url, format) {

        $(app).triggerHandler("serverBefore", [data]);

        url = url || app.url;
        failure = failure || app.defaultFailure;
        success = success || app.defaultSuccess;

        for (var name in data) {
            if (app.isElementObject(data[name], "INPUT")) {
                format = "FormData";
                break;
            }
        }

        if (typeof format == "string") {
            switch (format) {
                case "Json":
                    format = JSONFormat;
                    break;
                case "FormData":
                    format = FormDataFormat;
                    break;
            }
        }

        format = format || app.defaultFormat;

        if (typeof data == "object" && typeof success == "function" && typeof failure == "function" && typeof url == "string" && typeof format == "object") {

            if (typeof data.Command != "string") {
                throw "app.server : data.Command 无效";
            }

            var parameters = $.extend({ Token: app.token, ClientTime: app.now(), ContentType: "Json" }, data);

            for (var name in parameters) {
                if (parameters[name] === app.serverRemove || parameters[name] === undefined) {
                    delete parameters[name];
                }
            }

            var sign_names = [];

            for (var name in parameters) {
                sign_names.push(name);
            }

            sign_names.sort((x, y) => {

                x = comparison(x);
                y = comparison(y);

                return x.localeCompare(y);

                function comparison(value) {
                    // 基本参数最先签名。
                    switch (value) {
                        case "ClientTime":
                        case "Command":
                        case "ContentType":
                        case "Token":
                            return "          " + value;
                        default:
                            return value;
                    }
                }
            });

            if (parameters.Token) {

            }

            var sign_builder = new SignBuilder();

            $(sign_names).each(function () {
                sign_builder.append(this, parameters[this]);
            });

            parameters.Sign = sign_builder.computeSign();

            var uesBuffered = app.useBuffered(parameters.Command);
            var bufferedKey;

            if (uesBuffered) {
                bufferedKey = url;

                for (var name in parameters) {
                    switch (name) {
                        case "ClientTime":
                        case "Reload":
                        case "Verify":
                            continue;
                    }

                    bufferedKey += "&";
                    bufferedKey += name;
                    bufferedKey += "=";
                    bufferedKey += parameters[name];
                }

                bufferedKey = app.md5(bufferedKey);

                if (!parameters.Reload && app.buffered[bufferedKey]) {
                    setTimeout(function () {
                        processData(app.buffered[bufferedKey]);
                    });

                    return "buffered";
                }
            }

            $(app).triggerHandler("server", [parameters]);

            if (format == FormDataFormat && app.useIFrameSubmit) {

                var form = $("<form></form>");


                form.attr("action", url);
                form.attr("method", "POST");
                form.attr("enctype", "multipart/form-data");

                var iframe = $("<iframe></iframe>");

                app.appendToNoDisplay(iframe);

                function firstLoad() {
                    iframe.unbind("load", firstLoad);
                    iframe.bind("load", secondLoad);

                    $(this.contentWindow.document.body).append(form);

                    var data2 = format.encode(parameters);

                    for (var name in data2) {
                        switch (name) {
                            case "append":
                                continue;
                        }

                        if (app.isElementObject(data2[name], "INPUT")) {
                            var input = $(data2[name]);
                            input.attr("name", name);
                            form.append(input);
                        } else {
                            var input = $("<input/>");
                            input.attr("name", name);
                            input.val(data2[name]);
                            form.append(input);
                        }
                    }

                    form.submit();
                    form.submit();
                }

                function secondLoad() {
                    var responseText = $(this.contentWindow.document.body).text();

                    var decodeFormat = parameters.ContentType;

                    switch (decodeFormat) {
                        case "Json":
                            decodeFormat = JSONFormat;
                            break;
                    }

                    decodeFormat = decodeFormat || app.defaultFormat;

                    var result = decodeFormat.decode(responseText);

                    if (uesBuffered) {
                        app.buffered[bufferedKey] = result;
                    }

                    iframe.remove();

                    processData(result);
                }

                iframe.bind("load", firstLoad);

                return "iframe";
            }

            var httpRequest;

            if (window.XMLHttpRequest) {
                httpRequest = new XMLHttpRequest();
            }
            else {
                httpRequest = new ActiveXObject("Microsoft.XMLHTTP");
            }

            httpRequest.open("POST", url, app.serverAsync);

            if (format.contentType) {
                httpRequest.setRequestHeader("Content-Type", format.contentType);
            }

            function processData(result) {
                $(app).triggerHandler("serverAfter", [parameters, result]);

                switch (result[Consts.code]) {
                    case 0:
                        /* 成功 */
                        $(app).triggerHandler("serverSuccess", [parameters, result]);
                        success(result[Consts.data]);
                        break;
                    case 2:
                        /* ReToken */
                        $(app).triggerHandler("serverFailure", [parameters, result]);
                        app.makeToken();
                        app.server(data, success, failure, url, format);
                        break;
                    default:
                    case -1:
                        /* 失败 */
                        $(app).triggerHandler("serverFailure", [parameters, result]);
                        app.errMsg(result[Consts.msg]);
                        failure(result);
                        break;
                }
            }

            httpRequest.onreadystatechange = function () {
                if (this.readyState == 4) {
                    if (this.status == 200) {
                        var decodeFormat = parameters.ContentType;

                        switch (decodeFormat) {
                            case "Json":
                                decodeFormat = JSONFormat;
                                break;
                        }

                        decodeFormat = decodeFormat || app.defaultFormat;

                        var result = decodeFormat.decode(httpRequest.responseText);

                        if (uesBuffered) {
                            app.buffered[bufferedKey] = result;
                        }

                        processData(result);
                    } else {
                        /* HTTP 错误 */
                        failure({ code: this.status, Msg: httpRequest.responseText });
                    }
                }
            }

            httpRequest.send(format.encode(parameters));

            return "ajax";
        } else {
            throw "app.server : 参数格式错误";
        }
    },
    useBuffered: function (commandName) {
        // 因为缓存很多时候严重影响用户体验，所以暂时禁用。
        return false;
        //if (typeof commandName == "string") {
        //    return (commandName.startsWith("Read") || commandName.startsWith("Get"));
        //} else {
        //    throw "app.useBuffered : 参数格式错误";
        //}
    },
    md5: function (text) {
        return $.md5(text);
    },
    msg: function (text) {
        alert(text);
    },
    alert: function (text) {
        alert(text);
    },
    errMsg: function (text) {
        alert(text);
    },
    confirm: function (text, callback) {
        if (confirm(text)) {
            callback();
        }
    },
    tips: function (element, text) {
        alert(text);
    },
    isElementObject: function (object, tagName) {
        if (window.Element) {
            if (!(object instanceof window.Element)) {
                return false;
            }
        } else if (!object.document) {
            return false;
        }

        if (tagName && String(tagName).toUpperCase() != String(object.tagName).toUpperCase()) {
            return false;
        }

        return true;
    },
    verifies: {
        /** 非空验证 */
        notNull: function (element, name, value) {
            if (value === "" || value === null || value === undefined) {
                app.tips(element, name + " 不能为空！")
                return false;
            }
            return true;
        },
        /** 数字验证 */
        number: function (element, name, value) {
            if (!/^[0-9]+(\.[0-9]+)?$/.test(value) && value) {
                app.errMsg(name + " 只能填写数字!");
                return false;
            }
            return true;
        },
        /** 电话号码或手机号码 */
        mobileOrPhone: function (element, name, value) {
            var oldErrMsg = app.errMsg;
            app.errMsg = function () { };

            if (app.verifies.mobile(element, name, value)) {
                app.errMsg = oldErrMsg;
                return true;
            }

            if (app.verifies.phone(element, name, value)) {
                app.errMsg = oldErrMsg;
                return true;
            }

            app.errMsg = oldErrMsg;
            app.errMsg(name + " 不是有效的电话号码或手机号码！");
            return false;
        },
        /** 手机号码 */
        mobile: function (element, name, value) {
            if (!/^(\+([0-9]{2,4})-?)?1[0-9]{2}-?[0-9]{4}-?[0-9]{4}$/.test(value) && value) {
                app.errMsg(name + " 不是有效的手机号码！");
                return false;
            }
            return true;
        },
        /** 电话号码 */
        phone: function (element, name, value) {
            if (!/^([0-9]{3,4}-?)?[0-9]{3,4}-?[0-9]{3,4}$/.test(value) && value) {
                app.errMsg(name + " 不是有效的电话号码！");
                return false;
            }
            return true;
        },
        /** 身份证号码 */
        idNumber: function (element, name, value) {
            if (!/^[0-9]{17}[0-9xX]$/.test(value) && value) {
                app.errMsg(name + " 不是有效的身份证号码！");
                return false;
            }
            return true;
        }
    },
    appendToNoDisplay: function (element) {
        var noDisplayBox = $("#_NoDisplayBox");

        if (noDisplayBox.length == 0) {
            noDisplayBox = $("<div id=\"_NoDisplayBox\" style=\"position:absolute;left:-9999px;top:-9999px;\"></div>");

            app.body.append(noDisplayBox);
        }

        noDisplayBox.append(element);
    },
    showMenu: function (items, left, top, width) {
        var menu = $("<ul class=\"menu __menu\"></ul>");

        for (var i = 0; i < items.length; i++) {
            if (items[i] == ",") {
                menu.append($("<a class=\"separ\"></a>"))
            } else {
                var item = $("<li>" + items[i].text + "</li>");

                if (items[i].click) {
                    item.click(items[i].click);
                }

                menu.append(item);
            }
        }

        menu.click(function (e) {
            if ($(e.target).hasClass("separ")) {
                e.stopPropagation();
                return false;
            }
            if ($(e.target).hasClass("menu")) {
                e.stopPropagation();
                return false;
            }
        });

        if (left) {
            menu.css("left", (left - 5) + "px");
        }
        if (top) {
            menu.css("top", (top - 5) + "px");
        }
        if (width) {
            menu.css("width", width + "px");
        }

        if (left + width > $(window).innerWidth()) {
            menu.css("left", (left - width + 5) + "px");
        }

        app.body.append(menu);

        if (!window.__InitMenu) {
            window.__InitMenu = true;

            $(document).click(function () {
                app.removeMenus();
            });

            $(document).contextmenu(function (e) {
                if (app.removeMenus()) {
                    e.preventDefault();
                }
            });
        }

        return menu;
    },
    removeMenus: function () {
        return $(".__menu").remove().length;
    },
    frameSubmit: function (frame, data, callback) {

        /* 提交数据 */
        function internalFrameSubmit(frame, data, callback) {

            frame = $(frame);

            var command = frame.attr("command");

            data = data || {};
            var lengths = {};

            frame.find("[name]").addClass("temp-name-item");

            frame.find(".many .temp-name-item").removeClass("temp-name-item");

            frame.find(".temp-name-item").each(function () {
                var value = getValue(this);

                if (value == nullValue) {
                    return;
                }

                var name = $(this).attr("name");

                lengths[name] = lengths[name] ? lengths[name] + 1 : 1;

                if (lengths[name] == 1) {
                    data[name] = value;
                } else if (lengths[name] == 2) {
                    data[name] = [data[name], value];
                } else {
                    data[name].push(value);
                }
            });

            frame.find(".temp-name-item").removeClass("temp-name-item");

            try {
                frame.triggerHandler("process", [data]);
            } catch (e) {
                return false;
            }

            data.Command = command;

            app.server(data, function (Result) {
                var primaryValue = data[frame.attr("primary-key")] || Result[Consts.identity] || data[Consts.primaryKey];

                frame.find(".many .many .item").removeClass("item").addClass("temp-item");

                var manyItems = frame.find(".many .item");

                frame.find(".temp-item").removeClass("temp-item").addClass("item");

                if (manyItems.length && !primaryValue) {
                    app.errMsg(text + " 的 Many Frame 提交失败，因为该 Frame 没有设置主键(Primary-Key)属性");

                    return;
                }

                var index = -1;

                function submitMany() {

                    ++index;

                    if (index == manyItems.length) {
                        frame.triggerHandler("submitted");
                        callback(Result);
                        return;
                    }

                    var item = $(manyItems[index]);

                    var many = item.parents(".many:first");

                    var field = many.attr("field");
                    var text = many.attr("text");
                    var command = many.attr("command");

                    if (!field) {
                        app.errMsg(text + " Many Frame 提交失败，因为该 Many Frame 没有设置关联字段(Field)属性");

                        submitMany();

                        return;
                    }

                    var data = {};

                    data[field] = primaryValue;

                    item.attr("text", text);
                    item.attr("command", command);

                    internalFrameSubmit(item, data, function (Result) { submitMany(); });

                    item.removeAttr("text");
                    item.removeAttr("command");
                }

                submitMany();
            });
        }


        frame = $(frame);

        /* 判断是否有文件正在上传 */
        if (frame.find(".image-upload.ing").length) {
            app.alert("有图像正在上传，请稍后提交...")
            return false;
        }
        if (frame.find(".file-upload.ing").length) {
            app.alert("有图像正在上传，请稍后提交...")
            return false;
        }

        var command = frame.attr("command");

        var text = frame.attr("text");

        callback = callback || function () {
            app.msg(text + " 提交成功！");
        };

        if (!command) {
            app.errMsg(text + " Frame 提交失败，因为该 Frame 没有设置提交命令(Command)属性");
            return
        }

        var nullValue = {};

        function getValue(element) {
            if (element.tagName == "TEXTAREA") {
                return $(element).val();
            }
            if (element.tagName == "INPUT") {
                switch (element.type.toUpperCase()) {
                    case "CHECKBOX":
                    case "RADIO":
                        if (element.checked) {
                            return $(element).val();
                        }
                        return nullValue;
                    default:
                        return $(element).val();
                }
            }
            if (element.tagName == "SELECT") {
                return $(element).val();
            }

            return nullValue;
        }

        var verifyThrough = true;

        frame.find("[name][verify]").each(function () {
            var verify = $(this).attr("verify");

            if (!verify) {
                return;
            }

            var value = getValue(this);

            verify = verify.split(",");

            var name = $(this).attr("name");
            var field = $(this).attr("field") || $(this).attr("placeholder") || name;
            

            for (var j = 0; j < verify.length; j++) {
                var verifyFunction = app.verifies[verify[j]];

                if (!verifyFunction || typeof (verifyFunction) != "function") {
                    verifyThrough = false;

                    app.errMsg("验证器 " + verify[j] + " 未定义或无效");
                    return false;
                }

                if (!verifyFunction(this, field, value)) {
                    if (verifyThrough) {
                        $(this).focus();
                    }

                    verifyThrough = false;

                    break;
                }
            }
        });

        if (!verifyThrough) {
            return false;
        }

        /* 二次确认 */
        app.confirm("确认要保存数据吗？", function () {
            internalFrameSubmit(frame, data, callback);
        });

        return false;
    },
    setParams: function (resource, data) {
        var elements = $(".data-fill[resource=\"" + resource + "\"]");

        function getPropertyName(element) {
            switch (element.tagName) {
                case "INPUT":
                case "TEXTAREA":
                    return "value";
                default:
                    return "innerHTML";
            }
        }

        elements.each(function () {
            var propertyName = getPropertyName(this);

            var text = this._SetParams_Old_Text = (this._SetParams_Old_Text || this[propertyName]);

            var newText = app.format(text, data);

            if (newText != text) {
                this[propertyName] = newText;

                if ($(this).hasClass("only-first")) {
                    $(this).removeClass("only-first").removeClass("data-fill");
                }

                $(this).addClass("data-filled");
            }
        });
    },
    format: function (text, data) {
        var regex = /@([A-Za-z0-9_]+)/g;
        var result = "";
        var index = 0;
        var match;
        var hasMatch = false;

        while ((match = regex.exec(text))) {
            hasMatch = true;

            result += text.substring(index, match.index);
            result += data[match[1]];

            index = match.index + match[0].length;
        }

        if (hasMatch && index != text.length) {
            result += text.substring(index);
        }

        return result;
    },
    getDestElements: function (element, resource) {
        var resources = resource.split(",");
        var result = $([]);

        $(resources).each(function () {
            result = $.merge(result, app.getDestElement(element, "#" + this.trim()));
        });

        return result;
    },
    getDestElement: function (element, resource) {
        var parent = $(element).parent();

        while (parent.length > 0) {
            var result = parent.find(resource);

            if (result.length) {
                return result;
            }

            parent = parent.parent();
        }

        return $(resource);
    },
    controls: {
        table: function (element) {
            var me = $(element);
            var parameters = element.parameters;
            var isTable = element._IsTable || false;

            if (!parameters) {
                parameters = me.attr("parameters") || "";
            }

            if (typeof parameters === "string") {
                parameters = eval("({" + parameters + "})");
            }

            if (!parameters.Command) {
                parameters.Command = me.attr("command");
            }

            if (!parameters.Command) {
                throw "无法初始化表格，因为没有指定读取数据的命令。";
            }

            element.parameters = parameters;

            var columns = me.find("thead tr td[name]");

            var sorts = me.find("thead tr td[name].sort");

            if (!isTable) {

                /* 对名称包一层标签 */
                columns.each(function () {
                    var name = $(this).html();

                    $(this).html("");

                    $(this).append("<span class='name'>" + name + "</span>")
                });

                /* 表头右键菜单 */
                me.find("thead tr").contextmenu(function (e) {

                    e.stopPropagation();
                    e.preventDefault();

                    app.removeMenus();

                    var items = [];

                    columns.each(function () {

                        var column = $(this);

                        var checked = column.hasClass("hidden") ? "" : "checked='checked'";
                        var name = column.find(".name").text();

                        if (!name) {
                            return true;
                        }

                        if (items.length != 0) {
                            items.push(",");
                        }

                        items.push({
                            text: "<input type='checkbox' " + checked + " class='radio' style='float:left;'/><label class='label' style='display:block;text-align:center;padding-right:10px;'>" + name + "</label>",
                            click: function (e) {

                                e.stopPropagation();

                                var checkbox = $(this).find("input[type=checkbox]");

                                setTimeout(function () {
                                    var checked = !checkbox.prop("checked");

                                    checkbox.prop("checked", checked);

                                    if (checked) {
                                        column.removeClass("hidden");
                                    } else {
                                        column.addClass("hidden");
                                    }

                                    var tableData = me.prop("data");

                                    if (tableData) {
                                        rendererTable(tableData);
                                    } else {
                                        app.control(me);
                                    }
                                });

                                return false;
                            }
                        });
                    });

                    app.showMenu(items, e.clientX, e.clientY, 150);
                });

                /* 为排序列添加方向元素 */
                sorts.prepend("<span class=\"sort-dir\">▼</span>");

                /* 设置排序列的点击事件 */
                sorts.click(function () {
                    this._SortIndex = app._SortIndex = (app._SortIndex || 0) + 1;

                    var direction = $(this).find(".sort-dir");

                    if (direction.hasClass("asc")) {
                        direction.removeClass("asc").addClass("desc");
                    } else if (direction.hasClass("desc")) {
                        direction.removeClass("desc");
                    } else {
                        direction.addClass("asc");
                    }

                    if (direction.hasClass("desc")) {
                        direction.html("▲");
                    } else {
                        direction.html("▼");
                    }

                    app.controls.table(element);
                });

                /* 全选实现 */
                me.find("thead tr td[name=selection] input[type=checkbox]").click(function () {
                    if (this.checked) {
                        me.find("tbody tr td input[type=checkbox].selection:not(:checked)").click();
                    } else {
                        me.find("tbody tr td input[type=checkbox].selection:checked").click();
                    }
                });

                /* 获取当前选中的实现 */
                element.getSelected = function () {
                    var selectedRows = me.find("tbody tr.selected");
                    var result = [];

                    for (var i = 0; i < selectedRows.length; i++) {
                        result.push($.extend({}, selectedRows[i].data));
                    }

                    return result;
                };

                /* 对表头重新包一层盒子 */
                columns.each(function () {
                    var items = $(this).find(">*");

                    var box = $("<div class='t-title'></div>");

                    box.append(items);

                    $(this).append(box);
                });

                element._IsTable = true;
            }

            var orders = "";

            sorts.sort(function (before, after) {
                return (after._SortIndex || 0) - (before._SortIndex || 0);
            });

            sorts.each(function () {
                var direction = $(this).find(".sort-dir");

                if (direction.hasClass("asc")) {
                    orders += ",A" + $(this).attr("name");
                } else if (direction.hasClass("desc")) {
                    orders += ",D" + $(this).attr("name");
                }
            });

            if (orders) {
                parameters.Orders = orders.substr(1);
            } else {
                delete parameters.Orders;
            }

            var internalColumns = {
                selection: function (data, name, rowIndex, columnIndex) {
                    var me = $(this);

                    var result = document.createElement("input");

                    result.type = "checkbox";

                    $(result).addClass("selection");

                    $(result).click(function () {
                        var row = $(me.find("tbody tr")[rowIndex]);

                        var selection = me.find("thead tr td[name=selection] input[type=checkbox]");

                        if (this.checked) {
                            row.addClass("selected");

                            if (me.find("tbody tr td input[type=checkbox].selection:checked").length == me.find("tbody tr td input[type=checkbox].selection").length) {
                                selection.prop("checked", true);
                            }
                        } else {
                            row.removeClass("selected");

                            selection.prop("checked", false);
                        }
                    });

                    me.find("input[type=checkbox]").prop("checked", false);

                    return result;
                },
                operation: true
            };

            if (app.disabledTableLoad) {
                return false;
            }

            me.triggerHandler("load", [parameters]);

            function rendererTable(data) {
                if (data.length) {
                    var firstRow = data[0];

                    app.setParams(element.id, { Count: data.length });

                    columns.each(function () {
                        var me = $(this);
                        var name = me.attr("name");

                        if (firstRow[name] === undefined && internalColumns[name] === undefined) {
                            me.addClass("temp");
                        } else {
                            me.removeClass("temp");
                        }
                    });

                    var tColumns = me.find("thead tr td[name]:not(:hidden)");

                    var tBody = $(document.createElement("tbody"));

                    for (var i = 0; i < data.length; i++) {
                        var tRow = $(document.createElement("tr"));
                        var tData = data[i];

                        tRow.prop("data", tData);

                        for (var j = 0; j < tColumns.length; j++) {
                            var column = tColumns[j];
                            var name = $(column).attr("name");
                            var td = $(document.createElement("td"));
                            var renderer = column._Renderer;

                            if (!renderer) {
                                renderer = $(column).attr("renderer");

                                if (renderer) {
                                    eval("renderer = (function(data, name, rowIndex, columnIndex){" + renderer + "})");
                                } else if (internalColumns[name]) {
                                    renderer = internalColumns[name];
                                } else {
                                    renderer = function (data, name, rowIndex, columnIndex) {
                                        return data[name];
                                    };
                                }

                                column._Renderer = renderer;
                            }

                            var value = renderer.apply(element, [tData, name, i, j]);

                            if (app.isElementObject(value)) {
                                td.append(value);
                                td.prop("title", value.innerText);
                            } else if (value && value.jquery) {
                                td.append(value);
                                td.prop("title", value.text());
                            } else {
                                td.text(value);
                                td.prop("title", value);
                            }

                            tRow.append(td);
                        }

                        tBody.append(tRow);
                    }

                    setBody(tBody);
                } else {
                    setText("-V- 没有数据");
                }
            }

            app.server(parameters, function (result) {
                me.prop("data", result);

                rendererTable(result);
            });

            function setBody(body) {
                me.find("tbody").remove();
                me.append(body);
            }

            function setText(text) {
                var columns = me.find("thead tr td[name]:not(:hidden)");

                setBody("<tbody><tr><td colspan='" + columns.length + "' style='height:100px;' >" + text + "</td></tr></tbody>");
            }

            setText("加载中...");
        },
        paging: function (element) {
            var me = $(element);
            var resource = $(me).attr("resource");
            var destElement = app.getDestElement(me, "#" + resource)[0];
            var destParameters = destElement.parameters;
            var data = {};
            var isPaging = element._IsPaging || false;
            var buttons = element._Buttons || $();

            $.extend(data, destParameters);
            var command = me.attr("command");

            if (!command) {
                throw "无法初始化分页工具，因为没有指定读取命令。";
            }

            data.Command = command;

            var begin = data.Begin;
            var end = data.End;
            var pageSize = end - begin + 1;

            delete data.Begin;
            delete data.End;

            function computeMaxPage(total, pageSize) {
                return (total % pageSize == 0 ? total / pageSize : parseInt(total / pageSize) + 1) || 1;
            }

            function refreshButtons(pageSize, current, total) {
                app.setParams(element.id, { Total: total, PageSize: pageSize, Current: current });

                var maxPage = computeMaxPage(total, pageSize);
                var midIndex = parseInt(buttons.length / 2) + 1;
                var index = current < midIndex ? 1 : current + buttons.length - midIndex > maxPage ? maxPage - buttons.length + 1 : current - midIndex + 1;

                buttons.each(function () {
                    var me = $(this);

                    me.html(String(index));

                    if (index > maxPage || index < 1) {
                        me.addClass("temp");
                    } else {
                        me.removeClass("temp");
                    }

                    if (index == current) {
                        me.addClass("current");
                    } else {
                        me.removeClass("current");
                    }

                    ++index;
                });

                me.find(".max-page").html("共 " + maxPage + " 页");
            }

            function gotoPage(value) {
                var parameters = destElement.parameters;

                var begin = parameters.Begin;
                var end = parameters.End;

                var pageSize = end - begin + 1;
                var current = parseInt(end / pageSize);

                var total = element.Total;
                var maxPage = computeMaxPage(total, pageSize);

                switch (value) {
                    case "previon":
                        value = current - 1;
                        break;
                    case "first":
                        value = 1;
                        break;
                    case "next":
                        value = current + 1;
                        break;
                    case "last": ;
                        value = total;
                        break;
                }

                value = parseInt(value);

                if (isNaN(value)) {
                    value = current;
                }

                if (value < 1) {
                    value = 1;
                }

                if (value > maxPage) {
                    value = maxPage;
                }

                if (value != current) {
                    parameters.Begin = (value - 1) * pageSize + 1;
                    parameters.End = value * pageSize;

                    app.control($(destElement));

                    refreshButtons(pageSize, value, total);
                }
            }

            app.server(data, function (Result) {
                var current = parseInt(end / pageSize);
                var total = destElement.Total = element.Total = Result.Total;

                refreshButtons(pageSize, current, total);
            });

            if (!isPaging) {
                var gotoBox = $("<div class=\"filter right\"></div>");
                var gotoForm = $("<form></form>");
                var gotoNumber = $("<input class=\"left input\" type=\"text\" placeholder=\"跳转到...页\" />");
                var gotoButton = $("<input class=\"right button\" type=\"submit\" value=\"GO\" />");

                gotoForm.append(gotoNumber);
                gotoForm.append(gotoButton);
                gotoBox.append(gotoForm);

                gotoForm.submit(function (e) {
                    e.stopPropagation();
                    return false;
                });

                gotoButton.click(function () {
                    gotoPage(gotoNumber.val());

                    gotoNumber.val("");
                });


                var buttonBox = $("<div class=\"right inline-block\"><div>");
                var firstPage = $("<a class=\"tool button\">第一页</a>");
                var previonPage = $("<a class=\"tool button\">上一页</a>");
                var nextPage = $("<a class=\"tool button\">下一页</a>");
                var lastPage = $("<a class=\"tool button\">最后一页</a>");
                var buttonsCount = parseInt(me.attr("buttonsCount")) || 8;

                buttonBox.append(firstPage);
                buttonBox.append(previonPage);

                for (var i = 1; i <= buttonsCount; ++i) {
                    var item = $("<a class=\"tool button __number\">" + i + "</a>");

                    buttonBox.append(item);
                }
                
                buttonBox.append(nextPage);
                buttonBox.append(lastPage);

                buttons = buttonBox.find("a.__number");

                buttons.removeClass("__number");

                firstPage.click(function () {
                    gotoPage("first");
                });
                previonPage.click(function () {
                    gotoPage("previon");
                });
                nextPage.click(function () {
                    gotoPage("next");
                });
                lastPage.click(function () {
                    gotoPage("last");
                });
                buttons.click(function () {
                    gotoPage($(this).text());
                });

                var pageSizeBox = $("<div class=\"filter\"><label class=\"label\">每页显示数量：</label></div>");
                var pageSizeSelect = $("<select class=\"input left right\"><option>" + pageSize + "</option><option>10</option><option>15</option><option>20</option><option>25</option><option>30</option></select>");

                pageSizeBox.append(pageSizeSelect);
                pageSizeBox.append(" <label class=\"label max-page\"></label>");

                pageSizeSelect.change(function () {
                    var pageSize = parseInt($(this).val());
                    var total = element.Total;

                    var parameters = destElement.parameters;

                    parameters.Begin = 1;
                    parameters.End = pageSize;

                    app.control($(destElement));

                    refreshButtons(pageSize, 1, total);
                });

                me.append(gotoBox);
                me.append(buttonBox);
                me.append(pageSizeBox);

                me.addClass("paging");
                me.addClass("tools");

                element._Buttons = buttons;
                isPaging = element._IsPaging = true;
            }
        },
        content: function (element) {
            var me = $(element);

            me.find(".tools .refresh").click(function () {
                var resource = $(this).attr("resource");

                app.getDestElements($(this), resource).each(function () {
                    if (this.parameters) {
                        this.parameters.Reload = true;
                    }

                    app.control($(this));

                    if (this.parameters) {
                        delete this.parameters.Reload;
                    }
                });
            });

            me.find(".tools .insert").click(function () {
                var resource = $(this).attr("resource");
                var command = $(this).attr("command");
                var frame = $(this).attr("frame");

                frame = $(document.getElementById(frame));

                if (command) {
                    frame.attr("command", command);
                }

                app.getDestElements($(this), resource).each(function () {
                    var me = this;
                    var flag = app.show(frame);

                    /* 刷新 */
                    flag.on("submitted", function () {
                        if (me.parameters) {
                            me.parameters.Reload = true;
                        }

                        /* 如果有分页控件，则一起刷新 */
                        var paging = app.getDestElement(me, ".paging[resource='" + me.id + "']");

                        app.control($(me));
                        app.control(paging);

                        if (me.parameters) {
                            delete me.parameters.Reload;
                        }

                        flag.find(".close").click();
                    });
                });
            });

            me.find(".tools .update").click(function () {
                var resource = $(this).attr("resource");
                var command = $(this).attr("command");
                var frame = $(this).attr("frame");

                frame = $(document.getElementById(frame));

                if (command) {
                    frame.attr("command", command);
                }

                app.getDestElements($(this), resource).each(function () {
                    var me = this;

                    var selected = this.getSelected();

                    if (selected.length == 0) {
                        app.errMsg("请选择您要修改的数据！");
                        return;
                    }

                    for (var i = 0; i < selected.length; i++) {
                        var flag = app.show(frame);

                        app.apply(flag, $.extend({}, selected[i]));

                        /* 刷新 */
                        flag.on("submitted", function () {
                            if (me.parameters) {
                                me.parameters.Reload = true;
                            }

                            app.control($(me));

                            if (me.parameters) {
                                delete me.parameters.Reload;
                            }
                        });
                    }
                });
            });

            me.find(".tools .delete").click(function () {
                var resource = $(this).attr("resource");
                var command = $(this).attr("command");
                var primaryKey = $(this).attr("primaryKey") || Consts.primaryKey;

                app.getDestElements($(this), resource).each(function () {
                    var me = this;

                    var selected = me.getSelected();

                    if (selected.length == 0) {
                        app.errMsg("请选择您要删除的数据！");
                        return;
                    }

                    app.confirm("您确定要删除这些数据吗？", function () {
                        var id = "";

                        for (var i = 0; i < selected.length; i++) {
                            id += ("," + selected[i][primaryKey]);
                        }

                        id = id.substr(1);

                        var data = { Command: command };

                        data[primaryKey] = id;

                        app.server(data, function () {
                            app.msg("删除成功！");

                            /* 刷新组件 */
                            if (me.parameters) {
                                me.parameters.Reload = true;
                            }

                            app.control($(me));

                            if (me.parameters) {
                                delete me.parameters.Reload;
                            }
                        }, function () {
                            /* 删除失败 */
                        });
                    });
                });
            });

            me.find(".tools .exportexcel").click(function () {
                var resource = $(this).attr("resource");
                var fileName = $(this).attr("fileName");

                if (fileName) {
                    fileName = eval("('" + fileName + "')");
                } else {
                    fileName = "无名称";
                }

                app.getDestElements($(this), resource).each(function () {
                    switch (this[Consts.control]) {
                        case "table":
                            /* 有一些浏览器添加 class 后 css 样式不会马上响应。 */
                            /* 但这些 css 样式在这里非常重要，所以这里做了一些特殊处理，使 css 样式立即响应。 */
                            /* 添加一个元素，然后把这个元素移除 */

                            var me = this;
                            $(me).addClass("operating");

                            var temp = $("<div class=\"temp\"></div>");

                            $(me).append(temp);

                            temp.remove();

                            exportTable(me);

                            $(me).removeClass("operating");

                            $(me).append(temp);

                            temp.remove();
                            break;
                        default:
                            app.errMsg("只支持对表格数据导出。");
                    }
                });

                function exportTable(me) {
                    var data = me.parameters;
                    var config = [];
                    var columns = $(me).find("thead tr td:not(.temp)");
                    var baseband = {
                        selection: true,
                        operation: true
                    };

                    $(columns).each(function () {
                        var name = $(this).attr("name");

                        if (baseband[name]) {
                            return;
                        }

                        config.push({
                            Name: name,
                            Display: $(this).find(".name").text(),
                            Width: $(this).width() * 2,
                            Row: this.parentElement.rowIndex,
                            Column: this.cellIndex,
                            RowSpan: this.rowSpan,
                            ColSpan: this.colSpan,
                            Renderer: $(this).attr("renderer")
                        });
                    });

                    for (var i = 0; i < config.length; i++) {
                        for (var name in config[i]) {
                            var val = config[i][name];
                            if (val === "" || val === null || val === undefined) {
                                delete config[i][name];
                            }
                        }
                    }

                    data = $.extend({}, data);
                    data.CommandName = data.Command;
                    data.Command = "exportExcel"
                    data.Config = JSON.stringify(config);
                    data.FileName = fileName;

                    if (me.Total > 1000) {
                        app.choose("当前数据较多，请选择您要导出数据范围：", {
                            "导出本页": function () {
                                app.server(data, download);
                            },
                            "导出前100行": function () {
                                data.Begin = 1;
                                data.End = 100;
                                app.server(data, download);
                            },
                            "导出前1000行": function () {
                                data.Begin = 1;
                                data.End = 1000;
                                app.server(data, download);
                            },
                            "导出所有": function () {
                                data.Begin = 1;
                                data.End = 0x7fffffff;
                                app.server(data, download);
                            }
                        }, 5, 500);
                    } else {
                        data.Begin = 1;
                        data.End = 1000;
                        app.server(data, download);
                    }

                    function download(result) {
                        /* 打开新窗口下载文件 */
                        window.open(result[Consts.url]);
                    }
                }
            });

            me.find(".tools .filter").submit(function (e) {
                e.stopPropagation();

                var element = $(this).find("[name]");
                var parameters = {};

                element.each(function () {
                    var names = $(this).attr("name").split(',');
                    var value = $(this).val();

                    $(names).each(function () {
                        if (value == "") {
                            parameters[this] = undefined;
                        } else {
                            parameters[this] = value;
                        }
                    });
                });

                var resource = $(this).attr("resource");

                app.getDestElements($(this), resource).each(function () {
                    if (this.parameters) {
                        for (var name in parameters) {
                            if (parameters[name] === undefined) {
                                delete this.parameters[name];
                            } else {
                                this.parameters[name] = parameters[name];
                            }
                        }

                        /* 如果有分页参数，则将页面定位在第一页 */
                        if (this.parameters.Begin && this.parameters.End) {
                            this.parameters.End = this.parameters.End - this.parameters.Begin + 1;
                            this.parameters.Begin = 1;
                        }
                    }

                    app.control($(this));
                });

                return false;
            });

            me.find(".toggle-filter").each(function () {
                var resource = $(this).attr("resource");

                var items = $(this).find(".item");

                items.click(function () {
                    if ($(this).hasClass("active")) {
                        return false;
                    }

                    items.removeClass("data-fill");

                    if ($(this).hasClass("display-count")) {
                        $(this).addClass("data-fill");

                        if (!$(this).hasClass("display-count-seted-text")) {
                            $(this).html($(this).html() + "(@Total)");

                            $(this).addClass("display-count-seted-text");
                        }
                    }

                    var parameters = eval("({" + $(this).val() + "})");

                    app.getDestElements($(this), resource).each(function () {
                        if (this.parameters) {
                            $.extend(this.parameters, parameters);

                            for (var name in parameters) {
                                if (parameters[name] === undefined) {
                                    delete this.parameters[name];
                                }
                            }

                            /* 如果有分页参数，则将页面定位在第一页 */
                            if (this.parameters.Begin && this.parameters.End) {
                                this.parameters.End = this.parameters.End - this.parameters.Begin + 1;
                                this.parameters.Begin = 1;
                            }
                        }

                        app.control($(this));
                    });
                });

                function displayCount() {
                    var activeItem = null;

                    app.disabledTableLoad = true;
                    app.serverAsync = false;

                    items.each(function () {
                        if ($(this).hasClass("active")) {
                            activeItem = this;
                        } else if ($(this).hasClass("display-count")) {
                            $(this).click();
                        }
                    });

                    if (activeItem) {
                        $(activeItem).click();
                    }

                    app.disabledTableLoad = false;
                    app.serverAsync = true;
                }

                if ($(this).find(".item.display-count").length > 0) {
                    setTimeout(displayCount);
                }
            });

            me.find(".toggle").each(function () {
                var items = $(this).find(".item");

                items.click(function (e) {
                    items.removeClass("active");

                    $(this).addClass("active");
                });
            });
        },
        tree: function (element) {
            var me = $(element);
            var root = me.hasClass("tree") ? me : me.parents(".tree");
            var parentNode = me.hasClass("tree") ? $([]) : me.parent("li");
            var parameters = element.parameters;
            var isTree = root.prop("_IsTree") || false;
            var primaryKey = me.attr("primaryKey") || Consts.primaryKey;

            var checkboxHTML = root.hasClass("checkbox") ? "<input type=\"checkbox\"/>" : "";
            var refreshHTML = root.hasClass("refresh") ? "<span class=\"icon icon icon-refresh refresh\"><span>" : "";
            var insertHTML = root.hasClass("insert") ? "<span class=\"icon icon icon-plus insert\"></span>" : "";
            var updateHTML = root.hasClass("update") ? "<span class=\"icon icon icon-edit update\"></span>" : "";
            var deleteHTML = root.hasClass("delete") ? "<span class=\"icon icon icon-remove delete\"></span>" : "";
            var saveState = root.hasClass("savestate");


            if (!parameters) {
                parameters = me.attr("parameters") || "";
            }

            if (typeof parameters === "string") {
                parameters = eval("({" + parameters + "})");
            }

            if (!parameters.Command) {
                parameters.Command = me.attr("command");
            }

            if (!parameters.Command) {
                throw "无法初始化树，因为没有指定读取数据的命令。";
            }

            element.parameters = parameters;

            if (!isTree) {
                root.each(function () {
                    this.getSelected = function () {
                        var selctedElement;

                        if ($(this).hasClass("checkbox")) {
                            selctedElement = $(this).find("li>.row>input[type=checkbox]:checked").parent().parent();
                        } else {
                            selctedElement = $(this).find("li.active");
                        }

                        var result = [];

                        selctedElement.each(function () {
                            result.push($.extend({}, this.data));
                        });

                        return result;
                    };
                });
            }

            var expandedIds = me.prop("__ExpandedIds");

            if (expandedIds) {
                me.removeAttr("__ExpandedIds");
            } else {
                expandedIds = [];

                var expandedDir = me.find("li.expanded.dir");

                expandedDir.each(function () {
                    if (this.data) {
                        expandedIds.push(this.data[primaryKey]);
                    }
                });
            }

            me.find(">*").remove();

            me.append("<li><div class=\"row\"><span class=\"name\">加载中...</span></div></li>");

            function appendNode(parentElement, data) {
                root.triggerHandler("process", [data]);

                var type = data.treeType == "node" ? "node" : "dir";

                var node = $("<li id=\"li" + data[primaryKey]+ "\"></li>");
                var row = $("<div class=\"row\"></div>");
                var state = $("<span class=\"state icon icon-caret-right\"></span>");
                var checkbox = $(checkboxHTML);
                var icon = $("<span class=\"icon " + (data.treeIcon || (type == "dir" ? "icon icon-folder-close" : "icon icon-file")) + "\"></span>");
                var text = $("<span class=\"name\">" + data.treeText + "</span>");
                var children = $("<ul></ul>");
                var right = $("<div class=\"right\"></div>");
                var refresh = $(refreshHTML);
                var insert = $(insertHTML);
                var update = $(updateHTML);
                var remove = $(deleteHTML);
                
                node.addClass(type);
                node.prop("data", data);

                node.append(row);

                row.append(right);

                if (type == "dir") {
                    node.append(children);
                    node.addClass("collapse");
                    row.append(state);
                }

                if (data.Checkbox !== false) {
                    row.append(checkbox);
                }

                row.append(icon);
                row.append(text);

                right.append(refresh);
                right.append(insert);
                right.append(update);
                right.append(remove);

                parentElement.append(node);

                if (data.TreeTitle) {
                    text.attr("title", data.TreeTitle);
                }
                if (data.Checked) {
                    checkbox.prop("checked", true);
                }
                if (data.Expanded) {
                    loadChildren(node, state, false);
                }
                if (expandedIds.indexOf(data[primaryKey]) >= 0) {
                    data.Expanded = true;

                    loadChildren(node, state, false, expandedIds);
                }

                state.click(function (e) {
                    loadChildren(node, state, false);
                    e.stopPropagation();
                });

                if (!checkboxHTML) {
                    row.click(function (e) {
                        if (root.find("li.active").length == 1 && node.hasClass("active")) {
                            return false;
                        }

                        root.find("li.active").removeClass("active");
                        node.addClass("active");
                        e.stopPropagation();

                        root.triggerHandler("selectedChange");
                    });
                }

                row.dblclick(function (e) {
                    loadChildren(node, state, false);
                    e.stopPropagation();
                });

                checkbox.click(function (e) {
                    e.stopPropagation();
                });

                checkbox.change(function () {
                    root.triggerHandler("selectedChange");
                });

                checkbox.dblclick(function (e) {
                    e.stopPropagation();
                });

                right.click(function (e) {
                    e.stopPropagation();
                });

                right.dblclick(function (e) {
                    e.stopPropagation();
                });

                refresh.click(function () {
                    if (node.hasClass("expanded")) {
                        loadChildren(node, state, true);
                    }

                    loadChildren(node, state, true);
                });

                update.click(function () {
                    root.each(function () {
                        var oldGetSelected = this.getSelected;

                        this.getSelected = function () {
                            return [$.extend({}, data)];
                        };

                        $(".update[resource=\"" + this.id + "\"]").click();

                        this.getSelected = oldGetSelected;
                    });
                });

                remove.click(function () {
                    root.each(function () {
                        var oldGetSelected = this.getSelected;

                        this.getSelected = function () {
                            return [$.extend({}, data)];
                        };

                        $(".delete[resource=\"" + this.id + "\"]").click();

                        this.getSelected = oldGetSelected;
                    });
                });

                insert.click(function () {
                    root.each(function () {
                        var oldGetSelected = this.getSelected;

                        this.getSelected = function () {
                            return [$.extend({}, data)];
                        };

                        $(".insert[resource=\"" + this.id + "\"]").click();

                        this.getSelected = oldGetSelected;
                    });
                });
            }

            function loadChildren(node, state, reload, expandedIds) {
                if (node.hasClass("dir")) {
                    if (node.hasClass("collapse")) {
                        var childrenElement = node.find(">ul");

                        if (!(childrenElement.hasClass("loaded") && saveState)) {

                            var childrenData = $.extend({}, parameters);

                            childrenData[primaryKey] = node.prop("data")[primaryKey];

                            if (reload) {
                                childrenData.Reload = true;
                            }

                            childrenElement.prop("parameters", childrenData);

                            if (expandedIds) {
                                childrenElement.prop("__ExpandedIds", expandedIds);
                            }

                            app.control(childrenElement, "tree");
                        }

                        state.removeClass("icon-caret-right").addClass("icon-caret-down");
                        node.removeClass("collapse").addClass("expanded");

                    } else {
                        state.addClass("icon-caret-right").removeClass("icon-caret-down");
                        node.removeClass("expanded").addClass("collapse");
                    }
                }
            }

            app.server(parameters, function (result) {

                me.addClass("loaded");

                me.find(">*").remove();

                $(result).each(function () {
                    appendNode(me, this);
                });


                if (parentNode.length) {
                    var parentState = parentNode.find(">.row>.state");

                    if (result.length) {
                        parentState.css("visibility", "visible");
                    } else {
                        parentState.css("visibility", "hidden");
                    }
                }
            });

            root.prop("_IsTree", true);
        },
        list: function (element) {
            var me = $(element);
            var parameters = element.parameters;
            var isList = element._IsList || false;
            var value = me.attr("value") || Consts.primaryKey;
            var display = me.attr("display") || Consts.displayValue;
            var noneHTML = me.hasClass("none");
            var oldValue = me.prop(Consts.controlValue) || me.val();

            if (!parameters) {
                parameters = me.attr("parameters") || "";
            }

            if (typeof parameters == "string") {
                parameters = eval("({" + parameters + "})");
            }

            if (!parameters.Command) {
                parameters.Command = me.attr("command");
            }

            if (!parameters.Command) {
                throw "无法初始化表格，因为没有指定读取数据的命令。";
            }

            element.parameters = parameters;

            me.find("*").remove();

            me.append("<option value=\"\"> -- 加载中... --</option>");

            function appendOption() {
                me.triggerHandler("process", [this]);

                if (this.removed === true) {
                    return;
                }

                var item = $("<option></option>")

                item.val(this[value]);
                item.html(this[display]);
                item.prop("data", this);

                me.append(item);
            }

            app.server(parameters, function (result) {
                oldValue = me.prop(Consts.controlValue) || me.val() || oldValue;

                me.find("*").remove();

                if (noneHTML) {
                    var noneData = {};

                    noneData[value] = "";
                    noneData[display] = " -- 未选择 -- ";

                    appendOption.apply(noneData);
                }

                $(result).each(appendOption);

                if (oldValue) {
                    me.val(oldValue);
                }
            });

            element._IsList = true;
        },
        imageUpload: function (element) {
            var me = $(element);

            var box = $("<div class=\"image-upload\"></div>");
            box.attr("style", me.attr("style"));

            me.removeAttr("style");
            me.css("display", "none");

            var preview = $("<div class=\"preview\"></div>");

            var img = $("<img class=\"img\"/>");



            var buttons = $("<div class=\"buttons\"></div>");

            var upload = $("<input class=\"button upload\" type=\"button\" value=\"上传\" />");
            var change = $("<input class=\"button change\" type=\"button\" value=\"修改\" />");
            var remove = $("<input class=\"button remove\" type=\"button\" value=\"删除\" />");
            var progress = $("<span class=\"progress\">正在上传图片...</span>");

            setImgSrc();

            function setImgSrc() {
                if (me.val()) {
                    img.attr("src", me.val());
                    box.addClass("ed");
                }
            }
            function selectFile(e) {
                e.stopPropagation();

                var file = $("<input type=\"file\" accept=\"image/*\"/>");

                file.change(function () {

                    /* 图片正在上传 */
                    box.addClass("ing");

                    app.server({ Command: "UploadImage", File: this }, function (Result) {
                        /* 图片上传完成 */
                        me.val(Result["File"]);
                        me.change();
                        img.attr("src", Result["File"]);
                        box.removeClass("ing").addClass("ed");

                        /* IFrame 提交 自动移除 */
                        try {
                            file.remove();
                        } catch (e) {

                        }
                    });
                });

                app.appendToNoDisplay(file);

                /* 兼容异步布局浏览器 */
                setTimeout(function () { file.click(); }, 100);
            }

            function removeFile(e) {
                e.stopPropagation();

                box.removeClass("ed");
                me.val("");
                me.change();
                img.removeAttr("src");
            }

            upload.click(selectFile);
            change.click(selectFile);
            remove.click(removeFile);

            /* 查看大图 */
            box.click(function () {
                if (me.val()) {
                    app.showPhoto(me.val());
                }
            });
            box.attr("title", "点击查看大图");

            me.change(setImgSrc);

            preview.append(img);
            buttons.append(upload).append(change).append(progress);

            if ((me.attr("verify") || "").indexOf("notNull") == -1) {
                buttons.append(remove)
            }

            box.append(preview).append(buttons);
            me.after(box);
        },
        fileUpload: function (element) {
            var me = $(element);

            var box = $("<div class=\"file-upload\"></div>");


            var upload = $("<a class=\"upload button\">上传</a>");
            var change = $("<a class=\"change button\">修改</a>");

            function selectFile(e) {
                e.stopPropagation();

                var file = $("<input type=\"file\" accept=\"*/*\"/>");

                file.change(function () {
                    box.addClass("ing");
                    me.val("文件正在上传...");

                    app.server({ Command: "UploadFile", File: this }, function (Result) {
                        me.val(Result["File"]);
                        me.change();

                        box.removeClass("ing").addClass("ed");

                        try {
                            file.remove();
                        } catch (e) {

                        }
                    });
                });

                app.appendToNoDisplay(file);

                /* 兼容异步布局浏览器 */
                setTimeout(function () { file.click(); }, 100);
            }
            function valueChange() {
                if (me.val()) {
                    box.addClass("ed");
                } else {
                    box.removeClass("ed");
                }
            }
            function downloadFile() {
                window.open(me.val());
            }
            function removeFile(e) {
                e.stopPropagation();

                box.removeClass("ed");
                me.val("");
                me.change();
            }

            upload.click(selectFile);
            change.click(selectFile);

            valueChange();
            me.change(valueChange);

            box.append(" ");
            box.append(upload);
            box.append(" ");
            box.append(change);

            if (me.hasClass("remove")) {
                me.removeClass("remove");
                var remove = $("<a class=\"remove button\">删除</a>");

                remove.click(removeFile);

                box.append(" ");
                box.append(remove)
            }

            if (me.hasClass("download")) {
                me.removeClass("download");

                var download = $("<a class=\"download button\">下载</a>");

                download.click(downloadFile);

                box.append(" ");
                box.append(download);
            }

            if (me.hasClass("time")) {
                me.removeClass("time");

                function matchTime(Text) {
                    var timeRegex = /([1-2][0-9]{3})([0-9]{2})([0-9]{2})([0-9]{2})([0-9]{2})([0-9]{2})/;

                    var array = timeRegex.exec(Text);

                    if (array) {
                        var y = parseInt(array[1]), MM = parseInt(array[2]) - 1, d = parseInt(array[3]), h = parseInt(array[4]), m = parseInt(array[5]), s = parseInt(array[6]);

                        var r = new Date(y, MM, d, h, m, s);

                        if (r.getFullYear() == y && r.getMonth() == MM && r.getDate() == d && r.getHours() == h && r.getMinutes() == m && r.getSeconds() == s) {
                            return Renderers.date(r);
                        }
                    }

                    return null;
                }

                var time = $("<span class=\"time\" style=\"margin-left:10px;\"></span>");

                box.append(" ");
                box.append(time);

                function meChange() {
                    var value = matchTime(me.val());

                    if (value) {
                        time.html("文件上传时间 : " + value);
                        time.removeClass("temp");
                    } else {
                        time.addClass("temp");
                    }
                }

                meChange();

                me.change(meChange);
            }

            me.after(box);

            box.prepend(me);
        },
        date: function (element) {
            var me = $(element);

            function meClick(e) {
                e.stopPropagation();

                if (element._IsShowPanel) {
                    setLocation($(element).prop("__DatePanel"), element);

                    return false;
                }

                element._IsShowPanel = true;

                removeOtherDatePanel();

                var value = me.val();

                if (value) {
                    value = new Date(Date.parse(value));
                    me.val(Renderers.date(value));
                    me.change();
                } else {
                    value = app.now();
                }

                var year = value.getFullYear();
                var month = value.getMonth() + 1;
                var may = value.getDate();

                var minDay = 1;

                var maxDay = new Date(year, month, 1);
                maxDay.setDate(0);
                maxDay = maxDay.getDate();

                var days = [];

                value.setDate(minDay);
                var minWeek = value.getDay();

                value.setDate(maxDay);
                var maxWeek = value.getDay();

                value.setDate(minDay - 1);

                // 当月日期之前
                for (var i = minWeek - 1, j = value.getDate() ; i >= 0; --i) {
                    days.push({ Value: j - i, Type: "last" });
                }

                /* 当月日期 */
                for (var i = minDay; i <= maxDay; i++) {
                    if (i == may) {
                        days.push({ Value: i, Type: "selected" });
                    } else {
                        days.push({ Value: i, Type:"" });
                    }
                }

                value.setDate(minDay);
                value.setMonth(month);

                /* 当月日期之后 */
                for (var i = maxWeek, j = value.getDate() ; i < 6; ++i, ++j) {
                    days.push({ Value: j, Type: "next" });
                }

                var daysHtml = "";

                for (var i = 0; i < days.length; i++) {
                    daysHtml += "<i class=\"" + days[i].Type + "\">" + days[i].Value + "</i>";
                }

                var panel = $("<div class=\"date-panel\"><div class=\"top\"><div class=\"year\"><input class=\"button\" type=\"button\" value=\"<\"/><input class=\"input\" type=\"text\" /><input class=\"button\" type=\"button\" value=\">\" />" +
                    "</div><div class=\"month\"><input class=\"button\" type=\"button\" value=\"<\" /><input class=\"input\" type=\"text\" /><input class=\"button\" type=\"button\" value=\">\" /></div></div><div class=\"content\"><div class=\"week\">" +
                    "<i>日</i><i>一</i><i>二</i><i>三</i><i>四</i><i>五</i><i>六</i></div><div class=\"days\">"
                    + daysHtml +
                    "</div></div><div class=\"bottom\"><input class=\"today button\" type=\"button\" value=\"今天\"/><input class=\"ok button\" type=\"button\" value=\"确定\" />" +
                    "<input class=\"cancel button\" type=\"button\" value=\"清空\" /></div></div>");


                function ok() {
                    element._IsShowPanel = false;
                    var day = panel.find("i.selected").html();
                    var value = new Date(year, month - 1, day);
                    me.val(Renderers.date(value));
                    me.change();
                    panel.remove();
                }
                function setLocation(panel, element) {
                    var clientRect = $(element).offset();
                    panel.css("top", (clientRect.top + $(element).outerHeight()) + "px");
                    panel.css("left", clientRect.left + "px");
                }

                panel.find("input[value='>']").click(function (e) {
                    e.stopPropagation();
                    var input = $(this.parentElement).find("input[type=text]");
                    var value = parseInt(input.val());
                    input.val(value + 1);
                    input.trigger("change");
                });
                panel.find("input[value='<']").click(function (e) {
                    e.stopPropagation();
                    var input = $(this.parentElement).find("input[type=text]");
                    var value = parseInt(input.val());
                    input.val(value - 1);
                    input.trigger("change");
                });
                panel.find(".year input[type=text]").change(function (e) {
                    year = parseInt($(this).val());
                    ok();
                    meClick(e);
                }).val(year);
                panel.find(".month input[type=text]").change(function (e) {
                    month = parseInt($(this).val());
                    ok();
                    meClick(e);
                }).val(month);
                panel.find(".days").bind("mousewheel", function (e) {
                    if ((event.wheelDelta || e.wheelDelta) < 0) {
                        ++month;
                    } else {
                        --month;
                    }
                    ok();
                    meClick(e);
                });
                panel.find(".today").click(function () {
                    element._IsShowPanel = false;
                    var now = app.now();
                    now.setHours(0);
                    now.setMinutes(0);
                    now.setSeconds(0);
                    now.setMilliseconds(0);
                    me.val(Renderers.date(now));
                    me.change();
                    panel.remove();
                });
                panel.find(".cancel").click(function () {
                    element._IsShowPanel = false;
                    me.val("");
                    me.change();
                    panel.remove();
                });
                panel.find(".ok").click(ok);
                panel.find(".days i").on("click", function (e) {
                    var i = $(this);
                    e.stopPropagation();

                    panel.find("i.selected").removeClass("selected");
                    i.addClass("selected");

                    var value = parseInt(this.innerHTML);

                    if (i.hasClass("last")) {
                        --month;
                    } else if (i.hasClass("next")) {
                        ++month;
                    }

                    ok();
                    meClick(e);
                });
                panel.click(function (e) {
                    e.stopPropagation();
                });

                app.body.append(panel);


                setLocation(panel, element);

                panel.prop("__Element", element);
                $(element).prop("__DatePanel", panel);

                function reLayoutDatePanel() {
                    $(".date-panel").each(function () {
                        if (this.__Element) {
                            setLocation($(this), this.__Element);
                        }
                    });
                }

                function removeOtherDatePanel() {
                    $(".date-panel").each(function () {
                        $($(this).prop("__Element")).prop("_IsShowPanel", false);

                        $(this).remove();
                    });
                }

                if (!window.__InitDatePanel) {

                    window.__InitDatePanel = true;

                    $(document).click(removeOtherDatePanel);

                    $(window).resize(reLayoutDatePanel);

                    app.body.on("loadBody", function () {
                        $(this).find("#Content,.content,.item,.frame").scroll(reLayoutDatePanel);
                        $(this).find(".frame").on("move", reLayoutDatePanel);
                    });

                    $("#Content,.content,.item,.frame").scroll(reLayoutDatePanel);
                    $(".frame").on("move", reLayoutDatePanel);
                }
            }

            me.click(meClick);
        }
    },
    control: function (elements, controlName) {
        $(elements).each(function () {
            controlName = controlName || this[Consts.control];

            this[Consts.control] = controlName;

            app.controls[controlName](this);
        });
    },
    loadBody: function (data, callback, disableScripts) {
        var body = app.body;

        $.ajax({
            url: data.Url, type: "GET", data: { Version: app.version }, success: function (html) {
                body.find(">*").remove();

                var page = document.createElement("div");

                body.append(page);

                page.innerHTML = html;

                page = $(page);

                var links = page.find("link");

                $(links).each(function () {
                    body.append(this);
                });

                var scripts = page.find("script");

                if (disableScripts) {
                    page.on("submit", function(e) {
                        e.stopPropagation();
                        return false;
                    });
                } else {
                    $(scripts).each(function () {
                        if (this.src) {
                            var scriptElement = document.createElement("script");

                            scriptElement.src = this.src;

                            body.append(scriptElement);
                        }

                        if (this.innerHTML) {
                            eval(this.innerHTML);
                        }
                    });
                }

                scripts.remove();

                if (!data.CancelRemoveUnauthorized) {
                    page.find("[Command]").addClass("unauthorized");

                    $(data.Items).each(function () {
                        page.find(this.Identifier).removeClass("unauthorized");
                    });

                    page.find(".unauthorized").remove();
                }

                app.contentExtend(page);
                app.contentLayout(page);

                body.triggerHandler("loadBody");

                if (typeof callback == "function") {
                    callback();
                }
            }
        });
    },
    makeToken: function () {

        var oldServerAsync = app.serverAsync;

        app.serverAsync = false;

        app.server({ Command: "MakeToken", ClientId: app.client_id, DeviceId: app.device_id }, function (ret) {
            app.token = ret[Consts.token];

            app.setStorage(Consts.token, app.token);
        });

        app.serverAsync = oldServerAsync;
    },
    makeDeviceId: function () {
        app.device_id = app.md5(Guid.newGuid());

        app.setStorage(Consts.deviceId, app.device_id);
    },
    getStorage: function (name) {
        try {
            var value = localStorage.getItem(name);

            if (value) {
                return value;
            }
        } catch (e) {

        }

        try {
            var value = $.cookie(name);

            if (value) {
                return value;
            }
        } catch (e) {

        }

        return null;
    },
    setStorage: function (name, value) {
        try {
            localStorage.setItem(name, value);
        } catch (e) {

        }

        try {
            $.cookie(name, value, { path: "/" });
        } catch (e) {

        }
    }
};

var Renderers = {
    details: function (frame) {

        frame = $(document.getElementById(frame));

        var detailsButton = $("<a class=\"link\">详情</a>");

        detailsButton.click(function () {
            var row = $(this).parents("tr:first");

            var data = row.prop("data");

            var flag = app.show(frame);
            
            var table = $(this).parents("table:first");

            var update = $(".update[Resource=\"" + table.attr("id") + "\"]");

            if (update && update.length) {
                var edit = $("<input type='button' class='button' value='编辑'>");

                edit.click(function () {
                    flag.remove();

                    row.addClass("selected");

                    update.click();

                    row.removeClass("selected");
                });

                flag.find(".bottom").prepend(edit);
            }

            app.apply(flag, $.extend({ Readonly: true }, data));
        });

        return detailsButton;
    },
    valueType: function (value) {
        return {
            "1": "Boolean",
            "2": "Int",
            "4": "Long",
            "8": "Float",
            "16": "Double",
            "32": "String",
            "64": "DateTime",
            "128": "TimeSpan",
            "256": "Guid",
            "512": "Object",
            "1024": "Ids",
            "2048": "Decimal"
        }[value] || "未定义的类型";
    },
    date: function (value) {
        if (value) {
            if (typeof(value) == "string") {
                value = Date.parse(value);
            }

            value = new Date(value);

            if (value.getHours() == 0 && value.getMinutes() == 0 && value.getSeconds() == 0) {
                return value.getFullYear() + "-" + (value.getMonth() + 1) + "-" + value.getDate();
            }

            return value.getFullYear() + "-" + (value.getMonth() + 1) + "-" + value.getDate() + " " + value.getHours() + ":" + value.getMinutes() + ":" + value.getSeconds();
        }

        return "-";
    },
    commandSign: function (value) {
        var html = [];

        if (value & 0x1) {
            html.push("<span class=\"command-sign\" title=\"这是一个查询命令\">Se</span>");
        }
        if (value & 0x2) {
            html.push("<span class=\"command-sign\" title=\"这是一个插入命令\">In</span>");
        }
        if (value & 0x4) {
            html.push("<span class=\"command-sign\" title=\"这是一个更新命令\">Up</span>");
        }
        if (value & 0x8) {
            html.push("<span class=\"command-sign\" title=\"这是一个删除命令\">De</span>");
        }
        if (value & 0x10) {
            html.push("<span class=\"command-sign\" title=\"这个命令需要授权\">Au</span>");
        }
        if (value & 0x20) {
            html.push("<span class=\"command-sign\" title=\"这个命令不能被用户调用\">No</span>");
        }
        if (value & 0x40) {
            html.push("<span class=\"command-sign\" title=\"这个查询命令返回的一定是对象，而不是数组。\">So</span>");
        }
        if (value & 0x80) {
            html.push("<span class=\"command-sign\" title=\"这是一个触发器\">Tr</span>");
        }
        if (value & 0x100) {
            html.push("<span class=\"command-sign\" title=\"这个查询命令使用内存缓存\">Mc</span>");
        }
        if (value & 0x200) {
            html.push("<span class=\"command-sign\" title=\"这个查询命令使用磁盘缓存\">Dc</span>");
        }

        return $(html.join(","));
    },
    triggerSign: function (value) {
        var html = [];

        if (value & 0x1) {
            html.push("<span class=\"command-sign\" title=\"前置触发器\">Be</span>");
        }
        if (value & 0x2) {
            html.push("<span class=\"command-sign\" title=\"后置触发器\">Af</span>");
        }
        if (value & 0x4) {
            html.push("<span class=\"command-sign\" title=\"通过命令 Id 绑定\">Ib</span>");
        }
        if (value & 0x8) {
            html.push("<span class=\"command-sign\" title=\"通过命令 Sign 绑定\">Sb</span>");
        }

        return $(html.join(","));
    },
    image: function (url) {
        return $("<img>").attr("src", url).addClass("cell-img").dblclick(function () { app.showPhoto(url) });
    }
};

var UTF8 = {
    encode: function (text, index, length) {
        var bytes = [];

        for (; index < length; ++index) {
            var c = text.charCodeAt(index);

            if (c <= 0x7F) {
                bytes.push(c);
            } else if (c <= 0x7FF) {
                bytes.push(0xC0 + (c >> 6));
                bytes.push(0x80 + (c & 0x3F));
            } else {
                bytes.push(0xE0 + (c >> 12));
                bytes.push(0x80 + ((c >> 6) & 0x3F));
                bytes.push(0x80 + (c & 0x3F));
            }
        }

        return bytes;
    },
    decode: function (bytes, index, length) {
        var text = "";

        for (; index < length; ++index) {
            var n = bytes[index];

            if (n <= 0x7F) {
                text += String.fromCharCode(n);
            } else if (n >= 0xE0) {
                text += String.fromCharCode(((n & 0xF) << 12) + ((bytes[++index] & 0x3F) << 6) + (bytes[++index] & 0x3F));
            } else {
                text += String.fromCharCode(((n & 0x1F) << 6) + (bytes[++index] & 0x3F));
            }
        }

        return text;
    }
};

var Base64 = new (function () {
    var encoding = UTF8;
    var digitals = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
    var digitalsMap = new Array(0xFF);

    for (var i = 0; i < digitalsMap.length; i++) {
        digitalsMap[i] = -1;
    }

    for (var i = 0; i < digitals.length; i++) {
        digitalsMap[digitals.charCodeAt(i)] = i;
    }

    this.encode = function (text) {
        var bytes = encoding.encode(text, 0, text.length);
        var result = "";
        
        for (var i = 0; true;) {
            var a = bytes[i++];
            var b = bytes[i++] || 0;
            var c = bytes[i++] || 0;

            result += digitals.charAt(a >> 2);
            result += digitals.charAt(((a << 4) & 0x30) | (b >> 4));

            if (i < bytes.length) {
                result += digitals.charAt(((b << 2) & 0x3E) | (c >> 6));
                result += digitals.charAt(c & 0x3F);
            } else {
                switch (bytes.length % 3) {
                    case 0:
                        result += digitals.charAt(((b << 2) & 0x3E) | (c >> 6));
                        result += digitals.charAt(c & 0x3F);
                        break;
                    case 1:
                        result += "==";
                        break;
                    case 2:
                        result += digitals.charAt(((b << 2) & 0x3E) | (c >> 6));
                        result += "=";
                        break;
                }
                break;
            }

        }
        return result;
    };

    this.decode = function (text) {
        var bytes = [];

        var length = text.length;

        while (digitalsMap[text.charCodeAt(length - 1)] === -1) {
            --length;
        }

        for (var i = 0; i < length;) {
            var a = digitalsMap[text.charCodeAt(i++)];
            var b = digitalsMap[text.charCodeAt(i++)];
            var c = digitalsMap[text.charCodeAt(i++)];
            var d = digitalsMap[text.charCodeAt(i++)];

            bytes.push((a << 2) | (b >> 4));
            bytes.push(((b & 0xF) << 4) | (c >> 2));
            bytes.push(((c & 0x3) << 6) | d);
        }

        return encoding.decode(bytes, 0, bytes.length - text.length + length);
    };
})();

$.extend(Array.prototype, {
    indexOf: function (item) {
        /* 忽略强制类型比较 */

        for (var i = 0; i < this.length; i++) {
            if (this[i] == item) {
                return i;
            }
        }

        return -1;
    }
});

if (!String.prototype.startsWith) {
    $.extend(String.prototype, {
        startsWith: function (text) {
            return this.indexOf(text) == 0;
        },
        endsWith: function (text) {
            return this.indexOf(text) == this.length - text.length;
        },
        trim: function () {
            return this.replace(/(^\s*)|(\s*$)/g, "");
        }
    });
}

if (isNaN(Date.parse("1997-12-18 00:00:00"))) {
    Date.baseParse = Date.parse;
    Date.parse = function (str) {
        var match = /^([0-9]{4})-([0-9]{1,2})-([0-9]{1,2})( ([0-9]{1,2}):([0-9]{1,2}):([0-9]{1,2}))?$/.exec(str);
        if (!match) {
            return Date.baseParse(str);
        }
        var year = parseInt(match[1]);
        var month = parseInt(match[2]);
        var day = parseInt(match[3]);
        var hour = parseInt(match[5]) || 0;
        var minute = parseInt(match[6]) || 0;
        var second = parseInt(match[7]) || 0;
        return new Date(year, month - 1, day, hour, minute, second).getTime();
    }
}

if (!window.FormData) {
    app.useIFrameSubmit = true;

    window.FormData = function () {
        this.append = function (name, value) {
            this[name] = value;
        }
    }
}

if (!window.JSON) {
    window.JSON = {
        stringify: function (obj) {
            if (typeof obj == "string") {
                var result = "";
                for (var i = 0; i < obj.length; i++) {
                    switch (obj.charAt(i)) {
                        case '\\':
                            result += "\\\\";
                            break;
                        case '"':
                            result += "\\\"";
                            break;
                        case '\n':
                            result += "\\n";
                            break;
                        case '\r':
                            result += "\\r";
                            break;
                        case '\t':
                            result += "\\t";
                            break;
                        default:
                            result += obj.charAt(i);
                    }
                }
                return '"' + result + '"';
            } else if (typeof obj == "number") {
                return String(obj);
            } else if (typeof obj == "boolean") {
                return obj ? "true" : "false";
            } else if (obj instanceof Date) {
                return '"' + obj.getFullYear() + "-" + (obj.getMonth() + 1) + "-" + obj.getDate() + " " + obj.getHours() + ":" + obj.getMinutes() + ":" + obj.getSeconds() + '"';
            } else if (obj instanceof Array) {
                var result = "";

                for (var i = 0; i < obj.length; ++i) {
                    result += ",";
                    result += JSON.stringify(obj[i]);
                }

                return "[" + result.substr(1) + "]";
            } else {
                var result = "";

                for (var name in obj) {
                    result += ",";
                    result += JSON.stringify(name);
                    result += ":";
                    result += JSON.stringify(obj[name]);
                }

                return "{" + result.substr(1) + "}"
            }
        },
        parse: function (text) {
            return eval("(" + text + ")");
        }
    };
}

if ((app.body.css("-webkit-user-select") ||
    app.body.css("-moz-user-select") ||
    app.body.css("-ms-user-select") ||
    app.body.css("user-select")) == undefined) {

    $(document).on("selectstart", function (e) {
        return { INPUT: true, TEXTAREA: true }[e.target.tagName] || false;
    });
}

$(window).resize(function () {
    $(".frame").each(function () {
        var me = $(this);

        if (me.hasClass("maximized")) {
            /* 只调用当前元素所绑定的 resize 事件。 */
            /* 父级的 resize 事件不会触发。 */
            me.triggerHandler("resize");
        }
    });
});

$(document).click(function (e) {
    var me = e.target;
    if (me.tagName == "LABEL") {
        me = $(me);
        var dest = me.attr("for");
        me = me.parent();
        while (dest && me.length) {
            var destElement = me.find("#" + dest);
            if (destElement.length) {
                destElement.click();
                e.stopPropagation();
                return false;
            }
            me = me.parent();
        }
    }
});